package proc

import (
	"encoding/json"
	"io"
	"log"
	"net/http"
	"sort"
	"strconv"
)

func ApiServ(port int, dir string) {
	http.Handle("/", http.FileServer(http.Dir(dir)))

	http.HandleFunc("/api/add", add)
	http.HandleFunc("/api/start", start)
	http.HandleFunc("/api/stop", stop)
	http.HandleFunc("/api/delete", delete)
	http.HandleFunc("/api/list", list)
	if err := http.ListenAndServe("0.0.0.0:"+strconv.Itoa(port), nil); err != nil {
		log.Println(err)
	}
}

func add(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application-json")
	b, err := io.ReadAll(r.Body)
	log.Println(string(b))
	if err != nil {
		writeErr(err.Error(), w)
		return
	}
	req := &AddReq{}
	err = json.Unmarshal(b, req)
	if err != nil {
		writeErr(err.Error(), w)
		return
	}

	if req.Key == "" {
		writeErr("Key required", w)
		return
	}
	if req.Path == "" {
		writeErr("Path required", w)
		return
	}

	_, ok := pMap.Load(req.Key)
	if ok {
		writeErr(req.Key+" existed", w)
		return
	}

	p := NewProc(req.Key, req.Path, req.Args, req.Dir, req.Background, req.KeepAlive)
	pMap.Store(p.Key, p)
	writeOk(w)
}

func start(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application-json")
	key := r.URL.Query().Get("key")
	val, ok := pMap.Load(key)
	if !ok {
		writeErr(key+" not exist", w)
	} else {
		p := val.(*Proc)
		err := p.start()
		if err != nil {
			writeErr(err.Error(), w)
			return
		}
		go p.wait()
		go p.readOut()
		go p.readErr()

		resp := &Resp1{
			Code: 200,
			Msg:  "ok",
			Data: p,
		}
		b, err := json.Marshal(resp)
		if err != nil {
			writeErr(err.Error(), w)
		} else {
			w.Write(b)
		}
	}
}

func stop(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application-json")
	key := r.URL.Query().Get("key")
	val, ok := pMap.Load(key)
	if !ok {
		writeErr(key+" not exist", w)
	} else {
		p := val.(*Proc)
		if !p.Exited {
			p.stop()
		}
		resp := &Resp1{
			Code: 200,
			Msg:  "ok",
			Data: p,
		}
		b, err := json.Marshal(resp)
		if err != nil {
			writeErr(err.Error(), w)
		} else {
			w.Write(b)
		}
	}
}

func delete(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application-json")
	key := r.URL.Query().Get("key")
	val, ok := pMap.Load(key)
	if !ok {
		writeErr(key+" not exist", w)
	} else {
		p := val.(*Proc)
		if !p.Exited {
			p.stop()
		}
		pMap.Delete(key)
		writeOk(w)
	}
}

func list(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application-json")
	s := make([]*Proc, 0)
	pMap.Range(func(key, value interface{}) bool {
		p := value.(*Proc)
		p.out()
		p.err()
		s = append(s, p)
		return true
	})
	sort.Slice(s, func(i, j int) bool {
		return s[i].Key < s[j].Key
	})
	resp := &Resp2{
		Code: 200,
		Msg:  "ok",
		Data: s,
	}
	b, err := json.Marshal(resp)
	if err != nil {
		writeErr(err.Error(), w)
	} else {
		w.Write(b)
	}
}

func writeErr(errstring string, w http.ResponseWriter) {
	log.Println(errstring)
	resp := &Resp0{
		Code: -1,
		Msg:  errstring,
	}
	b, err := json.Marshal(resp)
	if err != nil {
		w.Write([]byte(err.Error()))
	} else {
		w.Write(b)
	}
}

func writeOk(w http.ResponseWriter) {
	resp := &Resp0{
		Code: 200,
		Msg:  "ok",
	}
	b, err := json.Marshal(resp)
	if err != nil {
		w.Write([]byte(err.Error()))
	} else {
		w.Write(b)
	}
}

type Resp0 struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
}

type Resp1 struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
	Data *Proc  `json:"data"`
}

type Resp2 struct {
	Code int     `json:"code"`
	Msg  string  `json:"msg"`
	Data []*Proc `json:"data"`
}

type AddReq struct {
	Key        string   `json:"key"`
	Path       string   `json:"path"`
	Args       []string `json:"args"`
	Dir        string   `json:"Dir"`
	Background bool     `json:"background"`
	KeepAlive  bool     `json:"keepAlive"`
}
